
#include "AStage.h"
#include "AXML.h"
#include "AWorld.h"
#include "AStageLayer.h"
#include "ASpriteModel.h"
#include "ACache.h"
#include "AString.h"

void CAStage::update(ccTime dt)
{
	_frames++;
	_fpsTimer += dt;
	if (_fpsTimer > 1.0f)
	{
		_fps = _frames / _fpsTimer;
		_frames = 0;
		_fpsTimer = 0;
	}

	double dtBase = CCDirector::sharedDirector()->getAnimationInterval();
	dtBase = 1.0f / _fps;
	//_Trace("dt=%f, dtb=%f", dt, dtBase);
	_updateTime((float)dtBase);
	//if (!isPaused())
	onUpdate();
}

CAStage::CAStage(const char* name)
{
	GUARD_FUNCTION();

	_name = name;
	_loadState = SLS_Initialize;
	_pxml = null;
	_objectsLoaded = 0;
	_objectsCount = 0;
	_percentLoaded = 0;
	_posOffset = CCPointZero;
	_aryFocusedLayer = CCArray::arrayWithCapacity(2);
	_aryFocusedLayer->retain();
	_touchstate = kTouchStateUngrabbed;

	_pRenderGroup = null;

	_timeTotal = 0;
	_timeScale = 1.0f;
	resetTimer();

	_fps = 30.0f;
	_fpsTimer = 0;
	_frames = 0;

	ccColor4B c;
	c.r = c.g = c.b = 0; c.a = 0xff;
	//initWithColor(c);

	_sublayers = new CAStringMap<CAStageLayer>();

	_paudio = new CAAudio(this);
	_paudio->retain();

	CCLOGINFO("CAStage created:%s", _name.c_str());

	//setIsTouchEnabled(true);

	autorelease();
}

CAStage::~CAStage(void)
{
	if (_sublayers)
	{
		_sublayers->removeAllObjects();
		_sublayers->release();
		_sublayers = null;
	}

	if (null != _pRenderGroup) _pRenderGroup->release();
	_pRenderGroup = null;

	_aryFocusedLayer->removeAllObjects();
	_aryFocusedLayer->release();
	_aryFocusedLayer = null;
	_animOffset.clear();
	
	_paudio->release();
	_paudio = null;

	CCLOGINFO("CAStage released:%s", _name.c_str());
}

CCSpriteBatchNode* CAStage::getSpriteGroup(const CAString& texname)
{
	GUARD_FUNCTION();

	if (null == _pRenderGroup)
	{
		_pRenderGroup = new CAStringMap<CCSpriteBatchNode>();
	}
	CCSpriteBatchNode* p = (CCSpriteBatchNode*)_pRenderGroup->objectForKey(texname);
	if (null == p)
	{
		p = CCSpriteBatchNode::batchNodeWithFile(texname.c_str());
		_pRenderGroup->setObject(p, texname);
		CCNode::addChild(p);
	}
	return p;
}


//load sprite model here
int CAStage::_loadSpriteModel(CAXMLNode* pnode)
{
	GUARD_FUNCTION();

	CAString name = pnode->attr("id");
	CAString src = pnode->attr("src");

	CASpriteModel* psm = null;
	psm = (CASpriteModel*)CACache::sharedCache().hit(src.c_str(), CACHE_MODEL);
	if (null == psm)
	{
		psm = new CASpriteModel();
		psm->build(src.c_str());
		CACache::sharedCache().put(name.c_str(), psm, CACHE_MODEL);
		_Trace("model %s:%s loaded", name.c_str(), src.c_str());
	}
	//psm->release();
	
	return 0;
}

int CAStage::_createLayer(CAXMLNode* pnode)
{
	GUARD_FUNCTION();

	CAString id = pnode->attr("id");
	CAString zorder = pnode->attr("zorder");
	CAXMLNode* pnodeRef = null;
	if (pnode->hasAttr("ref"))
	{
		CAString ref = pnode->attr("ref");
		CAXMLNode* pxml = CAXML::parse(ref.c_str(), false);
		pnodeRef = pxml->firstnode("layer");
		_Assert(pnodeRef);
	}
	//id is used to identify which layer class will be instanced.
	CAStageLayer* pl = CAWorld::sharedWorld().createLayer(this, null, id.c_str());
	_Assert(NULL != pl);

	pl->build(pnode, pnodeRef);
	
	_sublayers->setObject(pl, pl->name());

	this->addChild(pl);
	
	return 0;
}

unsigned int CAStage::_getUnloadedResourcesCount()
{
	CAXMLNode* pxml = _pxml->firstnode("stage");

	CAXMLNodes* pnodesModel = pxml->nodes("model");
	unsigned int model_count = pnodesModel->count();
	CAXMLNodes* pnodesMusic = pxml->nodes("music");
	unsigned int music_count = pnodesMusic ? pnodesMusic->count() : 0;
	CAXMLNodes* pnodesSound = pxml->nodes("sound");
	unsigned int sound_count = pnodesSound ? pnodesSound->count() : 0;
	unsigned int count = model_count + music_count + sound_count;

	return count;
}

#define MODEL_PERCENT	(0.9f)
EStageLoadState CAStage::loadProgressively()
{
	GUARD_FUNCTION();

	switch (_loadState)
	{
	case SLS_Initialize:
		{
			GUARD_FIELD(SLS_Initialize);
			_Assert(null == _pxml);
			CAString res = _name;
			res += ".stage";
			_pxml = CAXML::parse(res.c_str(), true);
			//_pxml->retain();
			_loadState = SLS_LoadModels;
			_objectsLoaded = 0;
			_objectsCount = _getUnloadedResourcesCount();
		}
		break;
	case SLS_LoadModels:
		{
			GUARD_FIELD(SLS_LoadModels);

			_Assert(null != _pxml);
			CAXMLNode* pxml = _pxml->firstnode("stage");

			CAXMLNodes* pnodesModel = pxml->nodes("model");
			unsigned int model_count = pnodesModel->count();
			CAXMLNodes* pnodesMusic = pxml->nodes("music");
			unsigned int music_count = pnodesMusic ? pnodesMusic->count() : 0;
			CAXMLNodes* pnodesSound = pxml->nodes("sound");
			unsigned int sound_count = pnodesSound ? pnodesSound->count() : 0;

			CAXMLNode* pnode;
			if (_objectsLoaded < model_count)
			{
				//load models
				pnode = (CAXMLNode*)pnodesModel->objectAtIndex(_objectsLoaded);
				_loadSpriteModel((CAXMLNode*)pnode);
				_objectsLoaded++;
			}
			else if (_objectsLoaded < model_count + music_count)
			{
				//load music
				pnode = (CAXMLNode*)pnodesMusic->objectAtIndex(_objectsLoaded - model_count);
				_paudio->loadMusicItem(pnode);
				_objectsLoaded++;
			}
			else if (_objectsLoaded < model_count + music_count + sound_count)
			{
				//load sound
				pnode = (CAXMLNode*)pnodesSound->objectAtIndex(_objectsLoaded - model_count - music_count);
				_paudio->loadSoundItem(pnode);
				_objectsLoaded++;
			}
			else if (_objectsLoaded < _objectsCount)
			{
				unsigned int loaded = _loadUnknownFormatObject();
				_Assert(loaded > 0);
				_objectsLoaded += loaded;
				_Assert(_objectsLoaded <= _objectsCount);
			}
			else
			{
				_loadState = SLS_LoadLayers0;
			}
			_percentLoaded = (MODEL_PERCENT * _objectsLoaded / _objectsCount);
		}
		break;
	case SLS_LoadLayers0:
		_objectsLoaded = 0;
		_loadState = SLS_LoadLayers;
	case SLS_LoadLayers:
		{
			GUARD_FIELD(SLS_LoadLayers);

			_Assert(null != _pxml);
			CAXMLNode* pxml = _pxml->firstnode("stage");

			CAXMLNodes* pnodesLayer = pxml->nodes("layer");
			unsigned int count = pnodesLayer->count();
			if (_objectsLoaded < count)
			{
				CAXMLNode* pnodeLayer = (CAXMLNode*)pnodesLayer->objectAtIndex(_objectsLoaded++);
				_createLayer((CAXMLNode*)pnodeLayer);
				_percentLoaded = MODEL_PERCENT + ((1.0f - MODEL_PERCENT) * _objectsLoaded / count);
			}
			else
			{
				_loadState = SLS_Finished;
				_percentLoaded = 1.0f;
				_Assert(null != _pxml);
				_pxml = null;
			}
		}
		break;
	case SLS_Finished:
		{
			//do nothing else
			_Assert(null == _pxml);
		}
		break;
	}

	return _loadState;
}

CCPoint CAStage::getPointFromPercent(const CCPoint& pos, bool bFollowCamera)
{
	CCPoint ret = pos;
	CAWorld::percent2view(ret);
	pixel2game(ret, bFollowCamera);
	return ret;
}

void CAStage::setOffset(const CCPoint& pt, float intime)
{
	_animOffset.clear();
	if (intime < 0.0001f || CAUtils::almostEqual(pt.x, _posOffset.x) && CAUtils::almostEqual(pt.y, _posOffset.y))
	{
		_posOffset = pt;
		//_Trace("stage set new offset DIR .x=%.2f .y=%.2f", _posOffset.x, _posOffset.y);
		return;
	}
	_animOffset.init(getTimeNow(), intime, _posOffset, pt);
	return;
}

void CAStage::onEnter()
{
	resetTimer();

	CCNode::scheduleUpdate();

	CCLayer::onEnter();
	//this->setAnchorPoint(ccp(0.5f, 0.5f));
	//this->setScale(0.5f);
   // CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, true);
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, kCCMenuHandlerPriority, true);

	_paudio->playMusic();
}

void CAStage::onUpdate()
{
	if (_animOffset.isValid())
	{
		_posOffset = _animOffset.getValue(getTimeNow());
		//_Trace("stage set new offset .x=%.2f .y=%.2f", _posOffset.x, _posOffset.y);
	}
	static int clean = 0;
	clean++;
	if (0 == (clean % 60))
	{
		CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();
	}

	MAP_FOREACH(CAStageLayer*, psl, _sublayers, psl->onUpdate());

	_paudio->update();

	return;
}

void CAStage::onExit() 
{
	CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
	//CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
	CCNode::unscheduleUpdate();
	CCLayer::onExit();
}

void CAStage::onRestart()
{
	this->setTimeTotal(0);
	this->setTimeNow(0);
	this->setTimeDelta(0);
}

void CAStage::setFocus(CAStageLayer* pLayer, bool push)
{
	if (!push)
	{
		_aryFocusedLayer->removeAllObjects();
	}
	_aryFocusedLayer->addObject(pLayer);
	_Trace("set focus layer as %s", getFocus()->name().c_str());
}

CAStageLayer* CAStage::getFocus()
{
	unsigned int count = _aryFocusedLayer->count();
	if (count >= 1)
	{
		return (CAStageLayer*)_aryFocusedLayer->objectAtIndex(count - 1);
	}
	else
	{
		_Trace("can not locate any focused layer !");
	}
	return null;
}

/*
void CAStage::killFocus(CAStageLayer* pLayer)
{
	_Assert(_aryFocusedLayer->count() > 0);
	_aryFocusedLayer->removeObject(pLayer);
	_Trace("focus layer changed to:%s", getFocus() ? getFocus()->name().c_str() : "NA");
}
*/

void CAStage::_dipatchEvent(ETouchState ts, CCTouch* touch)
{
	GUARD_FUNCTION();

	CCPoint posnow = touch->locationInView();
    posnow = CCDirector::sharedDirector()->convertToGL(posnow);

	CCPoint posprev = touch->previousLocationInView();
    posprev = CCDirector::sharedDirector()->convertToGL(posprev);

	CAEventTouch* pet = new CAEventTouch(ts, posnow, posprev);
	
	CAStageLayer* focus = getFocus();
	if (null != focus)
	{
		focus->onEvent(pet);
	}
	else
	{
		_Trace("Warning !!! NO FOCUED Layer be found!");
	}
}

bool CAStage::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
	if (_touchstate != kTouchStateUngrabbed) return false;
	_touchstate = kTouchStateGrabbed;

	_dipatchEvent(_touchstate, touch);
	_Info("ccTouchBegan");

	return true;
}

void CAStage::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
	// If it weren't for the TouchDispatcher, you would need to keep a reference
	// to the touch from touchBegan and check that the current touch is the same
	// as that one.
	// Actually, it would be even more complicated since in the Cocos dispatcher
	// you get CCSets instead of 1 UITouch, so you'd need to loop through the set
	// in each touchXXX method.
	
	CCAssert(_touchstate == kTouchStateGrabbed, L"Touch - Unexpected state!");	
	_Info("ccTouchMoved");

	_dipatchEvent(kTouchStateMoving, touch);
}

void CAStage::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
	CCAssert(_touchstate == kTouchStateGrabbed, L"Touch - Unexpected state!");	
	_touchstate = kTouchStateUngrabbed;
	_Info("ccTouchEnded");
	_dipatchEvent(_touchstate, touch);
} 

#if 0
void CAStage::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
}

void CAStage::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent)
{
}

void CAStage::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
}
#endif

void CAStage::playMusic(const char* psz)
{
	if (null == _paudio)
		return;
	if (_paudio->isMusicMute())
		return;
	_paudio->switchMusic(psz);
}

void CAStage::playEffect(const char* psz, float volume)
{
	if (null == _paudio)
		return;
	if (_paudio->isSoundMute())
		return;
	_paudio->playSound(psz, volume);
}

bool CAStage::isMusicMute() const
{
	if (null == _paudio)
		return true;
	return _paudio->isMusicMute();
}

bool CAStage::isSoundMute() const
{
	if (null == _paudio)
		return true;
	return _paudio->isSoundMute();
}

void CAStage::enableMusic(bool e)
{
	if (null == _paudio)
		return;
	_paudio->enableMusic(e);
}

void CAStage::enableSound(bool e)
{
	if (null == _paudio)
		return;
	_paudio->enableSound(e);
}

void CAStage::onSystemEvent(ESystemEvent evt, void* data)
{
	switch(evt)
	{
	case SE_Pause:
		if (null != _paudio)
		{
			_paudio->pauseMusic();
		}
		break;
	case SE_Resume:
		if (null != _paudio)
		{
			_paudio->resumeMusic();
		}
		break;
	default:
		_Assert(false);
		break;
	}

	CAEvent* pevent = new CAEventSystem(evt, data);
	//CAStageLayer* player;
	MAP_FOREACH(CAStageLayer*, player, _sublayers,
	{
		player->onEvent(pevent);
	});
}
